"""
绘图和可视化
信息可视化叫绘图 是数据分析中最重要的工作之一 帮助我们找出异常值,必要的数据转换 得出有关模型的idea等
matplotlib 是一个用于创建出版质量图表的桌面绘图包

matplotlib的图像都位于Figure对象中 可以用plt.figure创建一个新的Figure
# # 使用numpy的arange函数创建一个包含0到9的数组
# data = np.arange(10)
#
# # 打印数组内容，用于调试或展示
# print(data)
#
# # 使用plot函数绘制数组data的图像，这里没有特别的注释，因为代码很简单
# plt.plot(data)
# # 显示绘制的图像，这里没有特别的注释，因为代码很简单
# plt.show()

# 创建一个新的图形对象
fig = plt.figure()

# 在2x2的网格中添加第一个子图
ax1 = fig.add_subplot(2, 2, 1)
# 在2x2的网格中添加第二个子图
ax2 = fig.add_subplot(2, 2, 2)
# 在2x2的网格中添加第三个子图
ax3 = fig.add_subplot(2, 2, 3)

# 绘制一个累加的随机数序列图
# 这里使用了numpy生成的50个随机数的累加序列，并使用了黑色虚线表示
# 使用'k--'，则表示黑色虚线，表示的是线段  如果这时执行一条绘图命令（如plt.plot([1.5, 3.5, -2, 1.6])），matplotlib就会在最后一个用过的subplot（如果没有则创建一个）上进行绘制，隐藏创建figure和subplot的过程
# 'k--' 是一个线型选项 用于告诉matplotlib绘制的线段是黑色虚线 上面那些由fig.add_subplot所返回的对象是AxesSubplot对象,直接调用它们的实例方法就可以在空着的格子里面画图
# 使用matplotlib库绘制一条折线图，展示随机数据的累积和
# 这里的随机数据是通过numpy库生成的，使用cumsum方法计算累积和
# 'k--'参数设置折线图的颜色为黑色，线型为虚线
# plt.plot(np.random.randn(50).cumsum(), 'k--')
#
# # 绘制一个直方图，展示随机数据的分布情况
# # np.random.randn(100)生成100个正态分布的随机数
# # bins参数指定直方图的条形数为20
# # color参数设置条形的颜色为黑色，alpha参数设置透明度为0.3，以便观察重叠部分
# ax1.hist(np.random.randn(100), bins=20, color='k', alpha=0.3)
#
# # 绘制一个散点图，展示数据点的分布
# # np.arange(30)生成0到29的序列，作为x轴坐标
# # y轴坐标基于x轴坐标加上一组正态分布的随机数乘以3，以增加数据的波动性
# ax2.scatter(np.arange(30), np.arange(30)+3*np.random.randn(30))

# 创建包含subplot网格的figure是非常常见的任务,matplotlib提供了一个更为方便的方法 plt.subplots方法 它可以创建一个新的Figure，并返回一个含有已创建的subplot对象的NumPy数组：
# 创建一个包含2行3列子图的图形对象
fig, axes = plt.subplots(2, 3)
# nrows
# subplot的行数
# ncols
# subplot的列数
# sharex
# 所有subplot.应该使用相同的X轴刻度（调节xim将会影响所有subplot)
# sharey
# 所有subplot.应该使用相同的Y轴刻度（调节ylim将会影响所有subplot)
# subplot_kw
# 用于创建各subplot的关键字字典
# **fig_kw
# 创建figure时的其他关键字，如plt.subplots(2,2,figsize=(8,6))

# 调整subplot的间距
# plt.subplots_adjust(left=0.1, bottom=0.1, right=0.9, top=0.9, wspace=0.2, hspace=0.2)
# wspace 参数控制子图之间的水平间距，取值范围在0到1之间，默认值为0.2
# hspace 参数控制子图之间的垂直间距，取值范围在0到1之间，默认值为0.2

# 将间距收缩到了0
fig, axes = plt.subplots(2, 2, sharex=True, sharey=True)
for i in range(2):
    for j in range(2):
        axes[i, j].hist(np.random.randn(500), bins=50, color='k', alpha=0.5)
plt.subplots_adjust(wspace=0, hspace=0)

# matplotlib的plot函数接收一组x和y坐标 还可以接收一个表示颜色和线型的字符串缩写
# 根据x和y绘制绿色虚线
# ax.plot(x,y, 'g--')

# ax.plot(x,y,linestyle = '--', color = 'g')
# 绘制累计求和图形
# plt.plot(randn(30).cumsum(), 'k--', color='g')

# 也可以将其写成更为明确的形式
# 使用plt.plot()函数绘制一个线图，展示随机生成数据的累计和
# 参数说明：
# - randn(30).cumsum(): 生成30个正态分布的随机数并计算其累计和，作为y轴数据
# - color='k': 设置线条颜色为黑色
# - linestyle='dashed': 设置线条样式为虚线
# - marker='o': 设置数据点标记为圆圈形状
# plt.plot(randn(30).cumsum(),color='k',linestyle='dashed', marker='o')

# 线性图中 非实际数据点默认是按线性方式插值
# 生成一个包含30个元素的随机数组，并计算其累积和
data = np.random.randn(30).cumsum()

# 绘制默认样式的折线图
plt.plot(data, 'k--', label='Default')
# 绘制使用'steps-post'样式的折线图，用于展示步进图的效果
plt.plot(data, 'k--', drawstyle='steps-post', label='steps-post')
# 添加图例，位置由matplotlib自动选择最佳位置
plt.legend(loc='best')

刻度 标签和图例
大多数的图表装饰项 其主要实现方式有二 使用过程型的pyplot接口
pyplot接口的设计目的就是交互使用 含有 xlim,xticks和xticklabels之类的方法 分别控制图表的范围 刻度位置 刻度标签
使用方式有以下两种
调用时不带参数 则返回当前的参数值 plt.xlim()返回当前的x轴绘图范围
调用时带参数 则设置参数值 plt.xlim([0,10])会将x轴的范围设置为0到10

# 设置标题,轴标签,刻度以及刻度标签
# 创建一个新的图形对象
fig = plt.figure()

# 在图形对象中添加一个子图
# 这里的参数(1,1,1)表示将图形划分为1行1列，当前子图位置在第1个
ax = fig.add_subplot(1,1,1)

# 绘制随机生成的数据的累积和
# np.random.randn(1000)生成1000个正态分布的随机数
# .cumsum()计算这些随机数的累积和
# ax.plot()绘制这些累积和的曲线图
ax.plot(np.random.randn(1000).cumsum())

# 要改变x轴刻度 最简单的办法是使用set_xticks()方法和set_xticklabels()方法 前者告诉matplotlib要将刻度放在数据范围中的那些位置
# 默认情况下这些刻度标签 可以通过set_xticklabels()方法来设置其他值做标签
# 设置x轴的刻度位置，以便于后续自定义刻度标签
ticks = ax.set_xticks([0, 250, 500, 750, 1000])

# 自定义x轴的刻度标签，并设置标签的倾斜角度和字体大小
# 这里使用了rotation参数来倾斜标签，以适应长标签或有限的空间
labels = ax.set_xticklabels(['one', 'two', 'three', 'four', 'five'],
                            rotation=30, fontsize='small')

# # 设置图表的标题，用于描述图表的内容
# ax.set_title('My first matplotlib plot')
#
# # 为x轴设置名称，这里命名为'Stages'，表示x轴代表的含义
# ax.set_xlabel('Stages')

# 轴的类有集合方法 可以批量设定绘图选项
props = {
    'title' : 'My first matplotlib plot',
    'xlabel' : 'Stages'
}
ax.set(**props)

# 添加图例  legend是另一种用于标识图表元素的重要工具,最简单的是在subplot传入label参数
# 创建一个新的图形对象fig
fig = plt.figure()

# 在图形fig中添加一个子图ax，参数(1,1,1)表示将图形划分为1行1列，当前子图位于第1个位置
ax = fig.add_subplot(1,1,1)

# 绘制三条随机生成的数据序列的累计和曲线
# 'k', 'g--', 'r.'分别设置了线条的颜色和样式：黑色实线、绿色虚线、红色点状线
# label参数为每条曲线设置标签，用于图例展示
ax.plot(randn(1000).cumsum(), 'k', label='one')
ax.plot(randn(1000).cumsum(), 'g--', label='two')
ax.plot(randn(1000).cumsum(), 'r.', label='three')

# 调用ax.legend() 或plt.legend()来自动创建图例
# loc='best'参数让matplotlib自动选择最佳的图例位置 默认位置为'best'  loc告诉matplotlib将图例放在哪个位置 'best'表示最佳位置 会选择最不碍事的位置
# 要从图例中去除一个或多个元素 不传入label 或 label= 'nolegend'
ax.legend(loc='best')

# 注解 text可以将文本绘制在图表的指定坐标(x,y)
# ax.text(x,y,'Hello World!',family='monospace',fontsize=10)

# 创建一个新的图形对象
fig = plt.figure()
# 在图形对象中添加一个子图
ax = fig.add_subplot(1, 1, 1)

# 从CSV文件中读取SPX指数数据，使用第一列作为索引并解析日期
data = pd.read_csv('../data/spx.csv', index_col=0, parse_dates=True)
# 提取'SPX'列作为后续分析的对象
spx = data['SPX']

# 在子图ax上绘制SPX指数的折线图，使用黑色实线风格
spx.plot(ax=ax, style='k-')

# 定义金融危机期间的重要日期和事件
crisis_data = [
    (datetime(2007, 10, 11), 'Peak of bull market'),
    (datetime(2008, 3, 12), 'Bear Stearns Fails'),
    (datetime(2008, 9, 15), 'Lehman Bankruptcy')
]

# 遍历每一个重要日期和事件，添加注释到图表中
for data, label in crisis_data:
    ax.annotate(label, xy=(data, spx.asof(data) + 75),
                xytext=(data, spx.asof(data) + 225),
                arrowprops=dict(facecolor='black', headwidth=4, width=2,
                                headlength=4), horizontalalignment='left',
                verticalalignment='top')

# 设置x轴的显示范围
ax.set_xlim(['1/1/2025','1/1/2011'])
# 设置y轴的显示范围
ax.set_ylim([600, 1800])
# 设置图表的标题，概括图表内容
ax.set_title('Important dates in the 2008-2009 financial crisis')


# 显示图形
plt.show()

# 利用plt.savefig() 将当前图表保存到文件 该方法相当于Figure对象的实例方法savefig()  将图表保存为SVG文件
plt.savefig('../images/fig1.svg')
# 文件类型是通过文件扩展名推断出来的。因此，如果你使用的是.pdf，就会得到一个PDF文件。我在发布图片时最常用到两个重要的选项是dpi（控制“每英寸点数”分辨率）和bbox_inches（可以剪除当前图表周围的空白部分）。要得到一张带有最小白边且分辨率为400DPI的PNG图片，

plt.savefig('../images/fig1.png', dpi=400, bbox_inches='tight')

使用pandas 和 seaborn绘图
matplotlib实际上是一种比较低级的工具。要绘制一张图表，你组装一些基本组件就行：数据展示（即图表类型：线型图、柱状图、盒形图、散布图、等值线图等）、图例、标题、刻度标签以及其他注解型信息。
在pandas中，我们有多列数据，还有行和列标签。pandas自身就有内置的方法，用于简化从DataFrame和Series绘制图形。另一个库seaborn（https://seaborn.pydata.org/），由Michael Waskom创建的静态图形库。Seaborn简化了许多常见可视类型的创建。

# 创建一个基于累计求和的随机数序列的Series对象
# 这里使用了numpy的随机数生成器生成10个正态分布的随机数，并对其进行累计求和
# 索引设置为0到100之间，步长为10的整数序列，以模拟某种序列数据
s = pd.Series(np.random.randn(10).cumsum(),index=np.arange(0,100,10))

# 绘制Series对象的折线图
# 这里利用pandas内置的plot方法直接绘制数据序列的图形，以便直观地观察数据趋势
s.plot()

# 该series对象的索引会被给matplotlib,并给以绘制x轴 可以通过 use_index = False 禁用该功能 X轴的刻度和界限可以通过xticks和xlim选项进行调节，Y轴就用yticks和ylim
# label
# 用于图例的标签
# ax
# 要在其上进行绘制的matplotlib subploti对象。如果没有设置，则使用当前 matplotlib subplot
# style
# 将要传给matplotlib的风格字符串（如'ko-')
# alpha
# 图表的填充不透明度(0到1之间)
# kind
# 可以是line'、bar'、barh'、kde'
# logy
# 在Y轴上使用对数标尺
# use_index
# 将对象的索引用作刻度标签
# rot
# 旋转刻度标签(0到360)】
# xticks
# 用作X轴刻度的值
# yticks
# 用作Y轴刻度的值
# xlim
# X轴的界限（例如[0,10]）
# ylim
# Y轴的界限
# grid
# 显示轴网格线（默认打开】

pandas的大部分绘图方法都有一个可选的ax参数，它可以是一个matplotlib的subplot对象。这使你能够在网格布局中更为灵活地处理subplot的位置。
# DataFrame的plot方法会在一个subplot中为各列绘制一条线 并自动创建图例
# 创建一个DataFrame对象，用于演示数据的累积求和效果
# 使用randn生成符合正态分布的随机数，并进行累积求和
# columns参数指定数据列的标签为'A', 'B', 'C', 'D'
# index参数设置行索引为0到100之间，步长为10的整数序列
df = pd.DataFrame(randn(10, 4).cumsum(0), columns=['A', 'B', 'C', 'D'], index=np.arange(0, 100, 10))

# 绘制DataFrame中数据的折线图
# 这里利用pandas的plot方法，快速直观地展示数据的变化趋势
df.plot()
# plot属性包含一批不同绘图的方法 df.plot()等价与df.plot.line()

# plot.bar() 和 plot.barh() 分别绘制水平和垂直的柱状图 Series和DataFrame的索引将会被用作x bar或 y barh刻度
# 创建一个包含两个子图的图形对象，用于展示不同的数据视图
fig, axes = plt.subplots(2, 1)

# 初始化一个包含16个随机数的Series对象，索引为字母'a'到'p'，用于后续的条形图绘制
data = pd.Series(np.random.rand(16), index=list('abcdefghijklmnop'))

# 绘制垂直条形图，使用黑色填充条形，透明度设置为0.7，以提供视觉上的层次感
data.plot.bar(ax=axes[0], color='k', alpha=0.7)

# 绘制水平条形图，同样使用黑色填充条形，透明度设置为0.7，提供不同的数据视角
data.plot.barh(ax=axes[1], color='k', alpha=0.7)


# 创建一个DataFrame对象，包含6行4列的随机数数据
# 设置索引为['one','two','three','four','five','six']，列名使用pd.Index设置为['A','B','C','D']，并命名为'Genus'
df = pd.DataFrame(np.random.randn(6,4),index=['one','two','three','four','five','six'],columns=pd.Index(['A','B','C','D'],name='Genus'))

# 调用DataFrame的plot方法绘制数据图表
# df.plot.bar()

# 设置stacked = True 即可为DataFrame生成堆积柱状图 这样每行的值就会被堆积在一起
df.plot.barh(stacked=True,alpha=0.5)


"""

# 导入matplotlib库的pyplot模块，用于绘图
import matplotlib.pyplot as plt
# 导入numpy库，用于创建和操作数组
import numpy as np
import pandas as pd
from numpy.random import randn
from datetime import datetime

# 使用read_csv将数据加载进来,然后根据日期和聚会规模创建一张交叉表
# 读取小费数据集
tips = pd.read_csv('../data/tips.csv')

# 计算每天不同就餐人数的频数
party_counts = pd.crosstab(tips['day'], tips['size'])

# 限制就餐人数在2到5人之间
party_counts = party_counts.loc[:, 2:5]

# 进行规格化,使得各行的和为1,并生成图表
party_pcts = party_counts.div(party_counts.sum(1), axis=0)
party_pcts.plot.bar()


# 显示图形
# 使用matplotlib的show函数来展示前面绘制的图形，暂停程序执行，直到图形窗口关闭
plt.show()



if __name__ == '__main__':
    pass
